import Axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import {
  PureHttpError,
  PureHttpRequestConfig,
  PureHttpResponse,
  requestConfig,
  RequestMethods,
  Result,
} from "./types.d";
import { stringify } from "qs";
import NProgress from "../progress";
import { formatToken, getToken } from "@/utils/auth";
import { loadEnv } from "@build/index";
import { checkStatus } from "@/utils/http/checkStatus";
import { message } from "@/utils/message";
import { useUserStoreHook } from "@/store/modules/user";

const { VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX } = loadEnv();
// 相关配置请参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
  baseURL: VITE_GLOB_API_URL + VITE_GLOB_API_URL_PREFIX,
  // 请求超时时间30s
  timeout: 30 * 1000,
  headers: {
    Accept: "application/json, text/plain, */*",
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest",
  },
  // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）
  paramsSerializer: {
    serialize: function (params) {
      return stringify(params, { arrayFormat: "repeat" });
    },
  },
};

class PureHttp {
  constructor() {
    this.httpInterceptorsRequest();
    this.httpInterceptorsResponse();
  }

  /** token过期后，暂存待执行的请求 */
  private static requests = [];

  /** 防止重复刷新token */
  private static isRefreshing = false;

  /** 初始化配置对象 */
  private static initConfig: PureHttpRequestConfig = {};

  /** 保存当前Axios实例对象 */
  private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);

  /** 重连原始请求 */
  private static retryOriginalRequest(config: PureHttpRequestConfig) {
    return new Promise(resolve => {
      PureHttp.requests.push((token: string) => {
        config.headers["Authorization"] = formatToken(token);
        resolve(config);
      });
    });
  }

  /** 请求拦截 */
  private httpInterceptorsRequest(): void {
    PureHttp.axiosInstance.interceptors.request.use(
      async (config: PureHttpRequestConfig) => {
        // 开启进度条动画
        NProgress.start();
        // 优先判断post/get等方法是否传入回掉，否则执行初始化设置等回掉
        if (typeof config.beforeRequestCallback === "function") {
          config.beforeRequestCallback(config);
          return config;
        }
        if (PureHttp.initConfig.beforeRequestCallback) {
          PureHttp.initConfig.beforeRequestCallback(config);
          return config;
        }
        return new Promise(resolve => {
          const data = getToken();
          if (data) {
            config.headers[data.cookieName] = data.cookieValue;
          }
          resolve(config);
        });
      },
      error => {
        return Promise.reject(error);
      }
    );
  }

  /** 响应拦截 */
  private httpInterceptorsResponse(): void {
    const instance = PureHttp.axiosInstance;
    instance.interceptors.response.use(
      (response: PureHttpResponse<Result>) => {
        const $config = response.config;
        const { data } = response;
        const { success, msg, code, desc } = data;

        // 关闭进度条动画
        NProgress.done();
        if (data && success) {
          // 优先判断post/get等方法是否传入回掉，否则执行初始化设置等回掉
          if (typeof $config.beforeResponseCallback === "function") {
            $config.beforeResponseCallback(response);
            return data.data;
          }
          if (PureHttp.initConfig.beforeResponseCallback) {
            PureHttp.initConfig.beforeResponseCallback(response);
            return data.data;
          }
          return data.data;
        } else {
          if ("F4010" === code) {
            message(msg, { type: "error" });
            useUserStoreHook().logOut();
          }
          const errorMsg = code + ":" + msg ? msg : desc;
          throw new Error(errorMsg);
        }
      },
      (error: PureHttpError) => {
        console.log(error);
        const $error = error;
        $error.isCancelRequest = Axios.isCancel($error);
        // 关闭进度条动画
        NProgress.done();
        // 所有的响应异常 区分来源为取消请求/非取消请求
        const { response, code, message } = $error || {};
        let errMessage: string;
        if (code === "ECONNABORTED" && message.indexOf("timeout") !== -1) {
          errMessage = "接口请求超时,请刷新页面重试!";
        } else if ($error?.toString?.() ?? ""?.includes("Network Error")) {
          errMessage = "网络异常，请检查您的网络连接是否正常!";
        } else {
          errMessage = checkStatus(response.status, message);
        }
        $error.message = errMessage;
        return Promise.reject($error);
      }
    );
  }

  /** 通用请求工具函数 */
  public request<T>(
    method: RequestMethods,
    url: string,
    params?: any,
    data?: any,
    axiosConfig?: PureHttpRequestConfig
  ): Promise<T> {
    const config = {
      method,
      url,
      params,
      data,
      ...axiosConfig,
    } as PureHttpRequestConfig;

    // 单独处理自定义请求/响应回掉
    return new Promise((resolve, reject) => {
      PureHttp.axiosInstance
        .request(config)
        .then((response: undefined) => {
          resolve(response);
        })
        .catch(e => {
          reject(e);
        });
    });
  }

  /** 单独抽离的post工具函数 */
  public post<R>(config: requestConfig) {
    return this.request<R>("post", config.url, config.params, config.data);
  }

  /** 单独抽离的get工具函数 */
  public get<R>(config: requestConfig) {
    return this.request<R>("get", config.url, config.params, config.data);
  }
}

export const http = new PureHttp();
